#! /usr/bin/env bash

# Copyright (c) 2006-2015, Universities Space Research Association (USRA).
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the Universities Space Research Association nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY USRA ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL USRA BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

if [ -z "$PLEXIL_HOME" ]
then
    echo Error: Please set environment variable PLEXIL_HOME
    echo to the full pathname of your 'plexil' or 'trunk' directory.
    echo Exiting.
    exit 1
fi

. $PLEXIL_HOME/scripts/plexil-setup.sh

validate_file()
{
    if [ -z $1 ]
    then
        echo "Error: missing filename for $2 option"
        usage
        exit 2
    elif [ ! -e $1 ]
    then
        echo "Error: $1 does not exist"
        exit 1
    elif [ -d $1 ]
    then
        echo "Error: $1 is a directory"
        exit 1
    elif [ ! -r $1 ]
    then
        echo "Error: $1 cannot be read"
        exit 1
    fi
}

validNum()
{
    if [ -z "$1" ]
    then
        echo "Error: Missing parameter for $2 option"
        exit 2
    elif (( $1 <= 0 ))
    then    	
  	    echo "Error: $1 is not a legal parameter for $2"
	    exit 2
    fi
}

warn()
{
   if [ -z "$quiet" ]
     then
       echo "$1"
   fi
}

checker()
{
    if [ ! -x $PLEXIL_HOME/scripts/checkPlexil ]
    then 
	    echo Error: $PLEXIL_HOME/scripts/checkPlexil not found.	
	    exit 1
    fi
    if [ ! -r $plan_nm ]
    then
        echo Error: File $plan_nm does not exist, or is not readable.
        exit 1
    fi
    echo "Checking plan..."
    $PLEXIL_HOME/scripts/checkPlexil $plan_nm
    if [ $? -gt 0 ]
    then
	    echo Error: $plan_nm contains type errors. See the checker output for details.
        exit 1
	fi
}

summarize()
{
    echo Running executive from $PLEXIL_HOME
    echo "  Plan:           $plan_nm"
    if [ -n "$library_dirs" ]
    then
        echo "  Library path:   $library_dirs"
    fi
    if [ -n "$libraries" ]
    then
        echo "  Libraries:      $libraries"
    fi
    if [ -n "$host_in" ]
    then
        echo "  Viewer host:    $host_in"
    fi
    if [ -n "$port_in" ]
    then
        echo "  Viewer port:    $port_in"
    fi
    echo
}

usage_text="Usage:\n\tplexiltest -p <plan> [-s <script file>] [-d <debug file>] [-L <library dir>]* [-l <library>]*\
 [-v [-h <hostname>] [-n <portnumber>] [-b]] [-log <log file>] [-ch] [-c <interface config>] \n"

usage()
{
    echo -e $usage_text
    echo -e "For more options enter 'plexiltest -help'"
}

help_text="Options:\n\
\n\
 -help \n\
\t	displays this message\n\
 -p | -plan <plan> \n\
\t	specify PLEXIL plan (required) \n\
 -s | -script <script> \n\
\t 	specifies a script file\n\
 -d | -debug <file name> \n\
\t 	debug file (default is Debug.cfg)\n\
 -L | -libraryPath <directory name>\n\
\t 	specifies a directory for finding plan library files\n\
 -q | -quiet \n\
\t 	Minimize output\n\
 -v | -viewer \n\
\t 	starts the Plexil Viewer\n\
 -h | -hostname <name> \n\
\t 	Name of remote host where Viewer is running\n\
 -n | -port <number> \n\
\t 	Port number to use for Viewer\n\
 -b | -blocking \n\
\t 	enables breakpoints in Viewer\n\
 -ch | -check \n\
\t 	runs the PLEXIL static checker prior to executing plan\n\
 -l | -library <file name>\n\
\t 	specifies a plan library (.plx file)\n\
 -? | -help \n\
\t	shows this message of options
 -a | -autorun \n\
\t  Runs the specified plan immediately at Viewer startup \n\
 --for-viewer \n\
\t  Runs the exec with the supplied options, without checking them, and without starting the Viewer. \n\
\t  Intended for use by the Plexil Viewer or other scripts."

if [ $PLEXIL_EXECUTABLE ]
then
    prog=$PLEXIL_EXECUTABLE
elif [ -f $PLEXIL_HOME/bin/TestExec ] &&  [ -x $PLEXIL_HOME/bin/TestExec ]
then
    prog=$PLEXIL_HOME/bin/TestExec
elif [ -f $PLEXIL_HOME/src/apps/TestExec/TestExec ] && [ -x $PLEXIL_HOME/src/apps/TestExec/TestExec ]
then
    prog=$PLEXIL_HOME/src/apps/TestExec/TestExec
else
    echo "Error: Test Executive executable not found, aborting"
    exit 1
fi

$PLEXIL_HOME/scripts/plexil-check-prog $prog

quiet=""
plan_nm="" 
plan_cmd="" 
script_name="" 
guess_script=""
script_cmd="" 
libraries="" 
library_dirs=""
debug_cmd=""
check=""
auto_run_cmd=""

if [ ! $1 ]
then
  usage
  exit 1
fi

while [ -n "$1" ] 
do
    save=$1
    case $1 in
	    -a | -autorun )
            auto_run_cmd=$1;;

	    -b | -blocking )
            block=$1;;	#enable viewer break point	

	    -ch | -check )
            check=$1;;
        
	    -d | -debug )
            shift
	        validate_file "$1" $save
            debug_cmd="-d $1";;

        --for-viewer)
            automate=$1;;

	    -h | -hostname )
            shift
	        host_in="-h $1";;	#viewer host name
        
	    -help )
            echo -e $usage_text
            echo -e $help_text
	        exit 0;;

	    -L | -libraryPath )
            shift
            if [ -z "$1" ]
            then
                echo "Error: missing directory name for $save option"
                usage
                exit 2
            elif [ ! -r "$1" ] || [ ! -d "$1" ]
            then
                echo "Error: $1 is not a readable directory."
                exit 1
            else
                library_dirs="$library_dirs$1 "
	            lib_dirs_cmd="$lib_dirs_cmd-L $1 "
            fi;;
        
	    -l | -library )
            shift
	        validate_file "$1" $save
	        libraries="$libraries -l $1"
	        lib_cmd="$lib_cmd-l $1 ";;

	    -log )
            shift
            log_file="-log $1";;

	    -n | -port )
            shift
	        validNum "$1" $save
            port_in="$1"
	        port_cmd="-n $1";;	#viewer port
        
	    -p | -plan )
            shift
            validate_file "$1" $save
	        plan_nm=$1
	        plan_cmd="-p $1";;

        -q | -quiet )
            quiet=$1;;
        
	    -s | -script )
            shift
            validate_file "$1" $save
	        script_name=$1;;
        
	    -v | -viewer )
            viewer=$1;;	#viewer listener enabled

	    -? )
            echo 'Error: unrecognized option' $1
	        usage
	        exit 2;;
    esac
    shift
done

if [ -n "$automate" ]
then
    # Just Do It - presume caller knows what it's doing.
    # We do not expect a local Viewer to use the -h option.
    if [ -z "$plan_nm" ]
    then
        echo Error: -p option is required.
        usage
        exit 2
    fi
    
    if [ -z "$script_name" ]
    then
        echo Error: -s option is required.
        usage
        exit 2
    fi
    
    if [ -n "$check" ]
    then
        checker
    fi
    if [ -z "$quiet" ]
    then
        summarize
    fi

    script_cmd="-s $script_name"

    echo "RUN_TE_PID$$"

    #echo "$prog $plan_cmd $script_cmd $lib_cmd $lib_dirs_cmd $debug_cmd $viewer $port_cmd $block $quiet"
    exec $prog $plan_cmd $script_cmd $lib_cmd $lib_dirs_cmd $debug_cmd $viewer $port_cmd $block $quiet
fi

# Check Viewer options
if [ -z "$viewer" ]
then
    # No local Viewer
    if [ -z "$host_in" ]
    then
        # Check local port, if supplied
        if [ -n "$port_in" ]
        then
            if ( echo `list_ports_in_use` | grep -q -v "\<${port_in}\>" )
            then
                echo "Error: No Viewer is listening on local port $port_in. Exiting."
                exit 1
            fi
        fi
    elif [ -z "$port_in" ]
    then
        echo "Error: Viewer host $host_in specified, but no port was supplied."
        usage
        exit 1
    fi
    # TODO: Bounds check port number
elif [ -n "$host_in" ]
then
    echo "Error: -h (remove Viewer host) and -v (local Viewer) may not be combined."
    usage
    exit 1
else
    # Local Viewer requested
    if [ -z "$port_in" ]
    then
        # Find open local port
        port_in=`find_open_port`
        if [ -z "$port_in" ]
        then
            echo "Unable to find an open local port for Viewer communications. Exiting."
            exit 1
        fi
        port_cmd="-n $port_in"
    elif ( echo `list_ports_in_use` | grep -q "\<${port_in}\>" )
    then
        echo "Error: Port $port_in is in use. Exiting."
        exit 1
    fi
fi

if [ -n "$host_in" ]
then
    host_cmd="-hostname $host_in"
fi

# Run default debug
if [ -z "$debug_cmd" ] && [ -z "$quiet" ] && [ -r "Debug.cfg" ]
then
    debug_cmd="-d Debug.cfg"
fi

# Check plan for errors
if [ -z "$viewer" ] && [ -n "$check" ]
then
    checker
fi

# The plan file's extension, used to compute guessed script name.
ext=".`echo $plan_nm | awk -F. '{print $2}'`"

# If no more arguments, or next argument -l, check for candidate scripts.
# Otherwise, next argument must be script.

if [ -n "$script_name" ]
then
    if [ ! -f "$script_name" ]
    then
        echo Error: Script $script_name does not exist.
        exit 1
    elif [ ! -r "$script_name" ]
    then
        echo Error: Script file $script_name is not readable.
        exit 1
    fi
else
    # Attempt to guess based on plan name
    script_candidate=`basename $plan_nm $ext`.psx
    if [ -e $script_candidate ]
    then
        script_name=$script_candidate
    elif [ -e scripts/$script_candidate ]
    then
        script_name=scripts/$script_candidate
    elif [ -e ../scripts/$script_candidate ]        
    then
        script_name=../scripts/$script_candidate
    fi
fi

if [ -z "$script_name" ]
then
    warn "No simulation script specified; using empty script."
    script_name=$PLEXIL_HOME/examples/empty.psx
fi

script_cmd="-s $script_name"

#separate command-line from plexexec output
echo

# Print summary if desired
if [ -z "$quiet" ] && [ -z "$viewer" ]
then
    summarize
fi

if [ -n "$viewer" ]
then
    # Launch the viewer, which will in turn invoke this script again with the automate flag
    #echo "plexil -plexiltest $plan_cmd $script_cmd $lib_dirs_cmd $lib_cmd $debug_cmd $host_cmd $port_cmd $check $block $auto_run_cmd"
    exec plexil -plexiltest $plan_cmd $script_cmd $lib_dirs_cmd $lib_cmd $debug_cmd $host_cmd $port_cmd $check $block $auto_run_cmd
else
    # User doesn't want viewer
    #echo "$prog $plan_cmd $script_cmd $lib_dirs_cmd $lib_cmd $debug_cmd $block"
    exec $prog $plan_cmd $script_cmd $lib_dirs_cmd $lib_cmd $debug_cmd $block
fi
